package com.xbongbong.saas.analytical.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasAppEntity;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasEsModel;
import com.xbongbong.paas.elasticsearch.util.EsHelper;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.EnableEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.model.PaasAppModel;
import com.xbongbong.paas.pojo.dto.FormDataRepeatDTO;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.pojo.vo.FormDataRepeatVO;
import com.xbongbong.paas.pojo.vo.PaasFormDataEsListVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.parent.data.FormDataEsService;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.errorcodes.ProductErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.form.pojo.dto.ChildProductDTO;
import com.xbongbong.pro.formdata.pojo.SaasNeedRedundantAttrPojo;
import com.xbongbong.pro.formexplain.pojo.dto.HandlerExplainDTO;
import com.xbongbong.pro.product.pojo.ProductWarehouseSavePojo;
import com.xbongbong.pro.product.pojo.StockNotifyPojo;
import com.xbongbong.saas.analytical.SaasValidateAnalyticalService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.ProductConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.ProductWarehouseEntity;
import com.xbongbong.saas.domain.entity.SpecificationEntity;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.enums.ProductNotifyEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.dictionary.BatchSeqOperationEnum;
import com.xbongbong.saas.enums.subform.ProductWarehouseSubFormEnum;
import com.xbongbong.saas.help.SaasDataInitHelp;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.ProductWarehouseModel;
import com.xbongbong.saas.model.SpecificationModel;
import com.xbongbong.saas.model.StockFlowBillModel;
import com.xbongbong.saas.model.WarehouseModel;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getDoubleOrDefaultFromFormData;
import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getIntegerOrDefaultFromFormData;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

/**
 * @author 吴峰
 * @date 2021/07/15 17:15
 */
@Service("productAnalyticalServiceImpl")
public class ProductValidateAnalyticalServiceImpl implements SaasValidateAnalyticalService {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(ProductValidateAnalyticalServiceImpl.class);
    
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private ProductWarehouseModel productWarehouseModel;
    @Resource
    private EsHelper esHelper;
    @Resource
    private SpecificationModel specificationModel;
    @Resource
    private WarehouseModel warehouseModel;
    @Resource
    private ProductModel productModel;
    @Resource
    private PaasEsModel paasEsModel;
    @Resource
    private StockFlowBillModel stockFlowBillModel;
    @Resource
    private FormDataEsService formDataEsService;
    @Resource
    private PaasAppModel paasAppModel;

    @Override
    public List<Integer> type() {
        return Arrays.asList(XbbRefTypeEnum.PRODUCT.getCode());
    }

    @Override
    public void analyticalData(ValidateDataDTO validateDataDTO) throws XbbException {
        beforeSave(validateDataDTO);
    }
    
    public void beforeSave(ValidateDataDTO validateDataDTO) throws XbbException {
        try {
            // 产品参数保存前对相关参数进行合理化校验
            parameterCheck(validateDataDTO);
            JSONObject data = validateDataDTO.getData();
            for (Map.Entry<String,Object> entry : SaasDataInitHelp.initProduct().entrySet()){
                SaasDataInitHelp.antDataSet(data,entry.getKey(),entry.getValue());
            }
            JSONArray deptArray = data.getJSONArray(ProductEnum.VISBLE_DEPT.getAttr());
            if (Objects.nonNull(deptArray)) {
                List<Object> deptList = deptArray.toJavaList(Object.class);
                validateDataDTO.setVisbleDept(deptList);
            }
            JSONArray userArray = data.getJSONArray(ProductEnum.VISBLE_USER.getAttr());
            if (Objects.nonNull(userArray)) {
                List<Object> userList = userArray.toJavaList(Object.class);
                validateDataDTO.setVisbleUser(userList);
            }
            data.remove(ProductEnum.VISBLE_DEPT.getAttr());
            data.remove(ProductEnum.VISBLE_USER.getAttr());

            JSONArray warehouseArr = data.getJSONArray(ProductEnum.WAREHOUSE.getAttr());
            Map<Long, String> warehouseNameMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Set<Long> newWarehouseIds = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            if (warehouseArr != null && !warehouseArr.isEmpty()) {
                for (int i = 0; i < warehouseArr.size(); i++) {
                    JSONObject jsonObject = warehouseArr.getJSONObject(i);
                    warehouseNameMap.put(jsonObject.getLong(StringConstant.SAAS_LINK_BUSINESS_ID), jsonObject.getString(StringConstant.SAAS_LINK_BUSINESS_NAME));
                    newWarehouseIds.add(jsonObject.getLong(StringConstant.SAAS_LINK_BUSINESS_ID));
                }
            }
            // 预警校验
            if (Objects.equals(data.getInteger(ProductEnum.IS_NOTIFY.getAttr()), 1)) {
                List<StockNotifyPojo> stockNotifyPojoList = new ArrayList<>();
                if (Objects.nonNull(data.getJSONArray(ProductEnum.STOCK_NOTIFY.getAttr()))){
                    stockNotifyPojoList = data.getJSONArray(ProductEnum.STOCK_NOTIFY.getAttr()).toJavaList(StockNotifyPojo.class);
                }
                Map<Long, List<String>> warehouseSpecMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (StockNotifyPojo stockNotifyPojo : stockNotifyPojoList) {
                    // 校验最低库存不能大于最高库存
                    if (Double.compare(stockNotifyPojo.getNum_3(), stockNotifyPojo.getNum_4()) > 0D) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205047);
                    }
                    JSONArray specArr = stockNotifyPojo.getArray_1();
                    stockNotifyPojo.setSpecJsonString(JSON.toJSONString(specArr));
                    Map<String, String> notifySpecMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    for (int i = 0; i < specArr.size(); i++) {
                        notifySpecMap.putAll(JSONObject.parseObject(specArr.getJSONObject(i).toJSONString(), new TypeReference<Map<String, String>>() {}));
                    }
                    stockNotifyPojo.setProductName(notifySpecMap.values().toString());
                    stockNotifyPojo.setWarehouseId(stockNotifyPojo.getText_1().getJSONObject(0).getLong(StringConstant.SAAS_LINK_BUSINESS_ID));
                    stockNotifyPojo.setWarehouseName(stockNotifyPojo.getText_1().getJSONObject(0).getString(StringConstant.SAAS_LINK_BUSINESS_NAME));
                    List<String> specList = warehouseSpecMap.getOrDefault(stockNotifyPojo.getWarehouseId(), new ArrayList<>());
                    // 校验不能维护重复预警信息（仓库+规格不能重复）
                    if (specList.contains(stockNotifyPojo.getSpecJsonString())) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205045, String.format(ProductErrorCodeEnum.API_ERROR_205045.getMsg(), stockNotifyPojo.getProductName(), stockNotifyPojo.getWarehouseName()));
                    }
                    specList.add(stockNotifyPojo.getSpecJsonString());
                    warehouseSpecMap.put(stockNotifyPojo.getWarehouseId(), specList);
                    // 校验仓库删除了预警还有
                    if (!warehouseNameMap.containsKey(stockNotifyPojo.getWarehouseId()) && !Objects.equals(stockNotifyPojo.getWarehouseId(), ProductConstant.ALL_WAREHOUSE_ID)) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205043, String.format(ProductErrorCodeEnum.API_ERROR_205043.getMsg(), stockNotifyPojo.getWarehouseName()));
                    }
                }
            }
            ProductWarehouseSavePojo productWarehouseSavePojo = new ProductWarehouseSavePojo(newWarehouseIds);
            if (Objects.nonNull(validateDataDTO.getDataId()) && !Objects.equals(validateDataDTO.getDataId(), ProductConstant.ALL_WAREHOUSE_ID)) {
                Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                param.put(ParameterConstant.CORPID, validateDataDTO.getCorpid());
                param.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
                param.put(ParameterConstant.PARENT_ID, validateDataDTO.getDataId());
                List<ProductWarehouseEntity> productWarehouseEntityList = productWarehouseModel.findEntitysWithWarehouseChecked(param,null);
                Set<Long> oldWarehouseIds = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Set<Long> copyWarehouseIds = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<Long, Double> warehouseStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                Map<Long, String> oldWarehouseNameMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (ProductWarehouseEntity productWarehouseEntity : productWarehouseEntityList) {
                    if (Objects.equals(productWarehouseEntity.getWarehouseChecked(),0)) {
                        //解绑分仓跳过
                        continue;
                    }
                    oldWarehouseIds.add(productWarehouseEntity.getWarehouseId());
                    copyWarehouseIds.add(productWarehouseEntity.getWarehouseId());
                    warehouseStockMap.put(productWarehouseEntity.getWarehouseId(), Arith.add(productWarehouseEntity.getNum(), warehouseStockMap.getOrDefault(productWarehouseEntity.getWarehouseId(), 0D)));
                    oldWarehouseNameMap.put(productWarehouseEntity.getWarehouseId(), productWarehouseEntity.getWarehouseName());
                }
                copyWarehouseIds.retainAll(newWarehouseIds);
                oldWarehouseIds.removeAll(copyWarehouseIds);
                newWarehouseIds.removeAll(copyWarehouseIds);
                // 去除全部仓库
                oldWarehouseIds.remove(ProductConstant.ALL_WAREHOUSE_ID);
                // 有仓库权限的人才可以删除仓库
                List<Long> permissionWarehouseIdList = warehouseModel.getWarehouseIdsByPermission(validateDataDTO.getLoginUser(), validateDataDTO.getLoginUser().getDepSet());
                Iterator<Long> iterator = oldWarehouseIds.iterator();
                List<Long> noPermissionWarehouseList = new ArrayList<>();
                while (iterator.hasNext()) {
                    Long id = iterator.next();
                    if (!permissionWarehouseIdList.contains(id)) {
                        iterator.remove();;
                        copyWarehouseIds.add(id);
                        noPermissionWarehouseList.add(id);
                    }
                }
                if (CollectionsUtil.isNotEmpty(oldWarehouseIds)) {
                    for (Long warehouseId : oldWarehouseIds) {
                        // 校验删除仓库需要库存为0
                        if (Double.compare(warehouseStockMap.get(warehouseId) , BasicConstant.ZERO_DOUBLE) != 0) {
                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205044, String.format(ProductErrorCodeEnum.API_ERROR_205044.getMsg(),  oldWarehouseNameMap.getOrDefault(warehouseId, "")));
                        }
                    }
                }
                productWarehouseSavePojo = new ProductWarehouseSavePojo(newWarehouseIds, oldWarehouseIds, copyWarehouseIds, noPermissionWarehouseList, productWarehouseEntityList);
            }
            SaasNeedRedundantAttrPojo saasNeedRedundantAttrPojo = new SaasNeedRedundantAttrPojo();
            saasNeedRedundantAttrPojo.setProductWarehouseSavePojo(productWarehouseSavePojo);
            validateDataDTO.setSaasNeedRedundantAttrPoJo(saasNeedRedundantAttrPojo);
            validateDataDTO.setData(data);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOGGER.error("productAnalyticalServiceImpl.beforeSave error", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    /**
     * 产品参数保存前对相关参数进行合理化校验
     *
     * @param validateDataDTO 前端传参数据
     * @throws XbbException 参数不符合要求错误提醒
     * @author Jian.Wang
     * @date 2019/1/30 15:32
     * @since v1.0
     */
    private void parameterCheck(ValidateDataDTO validateDataDTO) throws XbbException {
        JSONObject data = validateDataDTO.getData();
        Long dataId = validateDataDTO.getDataId();
        String corpid = validateDataDTO.getCorpid();

        //批次保质期和序列号互斥校验
        Integer batchControl = getIntegerOrDefaultFromFormData(data, ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr(), 0);
        Integer serialNumberControl = getIntegerOrDefaultFromFormData(data, ProductEnum.ENABLE_SERIAL_NUMBER.getAttr(), 0);
        //保质期天数
        Long shelfLifeDays =  data.getLong(ProductEnum.SHELF_LIFE_DAYS.getAttr());
        // 到期预警天数
        Integer expirationAlertDays =  data.getInteger(ProductEnum.EXPIRATION_ALERT_DAYS.getAttr());
        boolean enableMultiUnit = Objects.equals(getIntegerOrDefaultFromFormData(data, ProductEnum.ENABLE_MULTI_UNIT.getAttr(), BasicConstant.ZERO), BasicConstant.ONE);
        String unit = data.getString(ProductEnum.UNIT.getAttr());
        //保质期开关
        Integer shelfLifeSwitch = 0;
        if (Objects.nonNull(shelfLifeDays)){
            shelfLifeSwitch = 1;
        }
        if (batchControl == 1 && serialNumberControl == 1){
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205074);
        }
        if (enableMultiUnit && StringUtil.isEmpty(unit)) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205100);
        }

        //保质期和预警天数共存校验
        if (StringUtil.isNotEmpty(data.getString(ProductEnum.EXPIRATION_ALERT_DAYS.getAttr())) && StringUtil.isEmpty(data.getString(ProductEnum.SHELF_LIFE_DAYS.getAttr()))) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205083);
        }
        //保质期天数必须大于到期预警天数校验
        if (Objects.equals(BasicConstant.ONE, batchControl) && Objects.equals(shelfLifeSwitch, BasicConstant.ONE) && expirationAlertDays != null){
            if (shelfLifeDays != null && shelfLifeDays <= expirationAlertDays){
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205076);
            }
        }
        //校验保质期天数必须大于等于0的整数，到期预警天数必须为大于0的整数
        if (Objects.equals(BasicConstant.ONE, batchControl) && Objects.equals(shelfLifeSwitch, BasicConstant.ONE)){
            if (shelfLifeDays != null && shelfLifeDays < 0){
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205077);
            }
            if(expirationAlertDays != null && expirationAlertDays <= 0){
                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205084);
            }
        }
        //批次保质期没有开启  移除保质期到期预警天数值
        if(Objects.equals(BasicConstant.ZERO, batchControl)){
            data.put(ProductEnum.SHELF_LIFE_DAYS.getAttr(), null);
            data.put(ProductEnum.EXPIRATION_ALERT_DAYS.getAttr(), null);
        }
        //产品可见部门校验
        JSONArray deptArray = data.getJSONArray(ProductEnum.VISBLE_DEPT.getAttr());
        if (Objects.nonNull(deptArray)) {
            List<Object> deptList = deptArray.toJavaList(Object.class);
            if (CollectionsUtil.isNotEmpty(deptList)) {
                for (Object o : deptList) {
                    if (!String.valueOf(o).chars().allMatch(Character::isDigit)) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205118);
                    }
                }
            }
        }
        // 产品名称长度校验
        int productNameMax = 100;
        String productName = FastJsonHelper.getStringOrDefaultFromFormData(data, ProductEnum.NAME.getAttr(), "");
        if (productName.length() > productNameMax) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205015);
        }
        // 产品编号长度校验
        int productNoMax = 50;
        String productNo = validateDataDTO.getSerialNo();
        if (productNo.length() > productNoMax) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205016);
        }
        Boolean noRepeat = productNoRepeat(productNo, validateDataDTO.getCorpid(), dataId);
        if (!noRepeat) {
            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205003);
        }
        // 校验分类
        FieldAttrEntity fieldAttrEntity = validateDataDTO.getExplainMap().get(ProductEnum.CATEGORY_ID.getAttr());
        FieldAttrEntity barcodeField = validateDataDTO.getExplainMap().get(ProductEnum.BARCODE.getAttr());
        HandlerExplainDTO handlerExplainDTO = new HandlerExplainDTO();
        handlerExplainDTO.setOwnerId(Collections.singletonList(validateDataDTO.getOwnerId()));
        BeanUtil.copyProperties(validateDataDTO, handlerExplainDTO, false);
        if (Objects.isNull(validateDataDTO.getCreatorId())) {
            handlerExplainDTO.setCreatorId(validateDataDTO.getUserId());
        } else {
            handlerExplainDTO.setCreatorId(validateDataDTO.getCreatorId());
        }
        if (Objects.nonNull(fieldAttrEntity)) {
            if (Objects.equals(1, fieldAttrEntity.getVisible())) {
                // 高级权限不可见时不校验数据
                Boolean visible = !ExplainUtil.invisibleOfScope(fieldAttrEntity, handlerExplainDTO);
                Boolean isEmpty = StringUtil.isEmpty(data.getString(fieldAttrEntity.getAttr())) || Objects.equals(data.getString(fieldAttrEntity.getAttr()), "0");
                if (visible && fieldAttrEntity.getRequired().equals(1) && isEmpty) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, fieldAttrEntity.getAttrName());
                }
            }
        }
        Boolean barcodeVisible = !ExplainUtil.invisibleOfScope(barcodeField, handlerExplainDTO);
        Boolean barcodeRequired = !Objects.isNull(barcodeField) && Objects.equals(barcodeField.getRequired(), 1);
        JSONObject specObj = data.getJSONObject(ProductEnum.SPECIFICATION.getAttr());
        int specValueMaxSize = 600;
        if (Objects.isNull(dataId)) {
            removeDataKey(data);
            // 新建产品对规格进行校验
            int nameMaxLength = 200;
            if (Objects.nonNull(specObj)) {
                boolean advancedMode = specObj.getBoolean(ProductConstant.ADVANCED_MODE);
                if (advancedMode) {
                    JSONArray specValueArray = specObj.getJSONArray("specValue");
                    JSONArray specDetailArray = specObj.getJSONArray("specDetails");
                    int specValueCount = 1;
                    for (int i = 0; i < specValueArray.size(); i++) {
                        JSONObject specValueObj = specValueArray.getJSONObject(i);
                        String name = specValueObj.getString("name");
                        JSONArray valueArray = specValueObj.getJSONArray("value");
                        if (name.length() > nameMaxLength) {
                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205057);
                        }
                        specValueCount = specValueCount * valueArray.size();
                        for (int j = 0; j < valueArray.size(); j++) {
                            String value = valueArray.getString(j);
                            if (value.length() > nameMaxLength) {
                                throw new XbbException(ProductErrorCodeEnum.API_ERROR_205058);
                            }
                        }
                    }
                    if (specValueCount > specValueMaxSize) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205059);
                    }
                    for (int i = 0; i < specDetailArray.size(); i++) {
                        JSONObject specDetailObj = specDetailArray.getJSONObject(i);
                        String code = specDetailObj.getString(ProductConstant.PRODUCT_CODE);
                        if (barcodeRequired && barcodeVisible && StringUtil.isEmpty(code)) {
                            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, barcodeField.getAttrName());
                        }
                    }
                } else {
                    String value = specObj.getString(ProductConstant.SINGLE_SPEC);
                    if (value != null && value.length() > nameMaxLength) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205057);
                    }
                    String code = data.getString(ProductEnum.BARCODE.getAttr());
                    if (barcodeRequired && barcodeVisible && StringUtil.isEmpty(code)) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FIELD_MUST_REQUIRED, barcodeField.getAttrName());
                    }
                }
            }
        } else {
            //编辑校验0库存下才能开启关闭启用批次保质期管理或者序列号管理
            ProductEntityExt parentProduct = productModel.getByKey(dataId, corpid);
            JSONObject oldData = parentProduct.getData();
            this.checkProductCost(parentProduct, data);
            Integer oldBatchControl = getIntegerOrDefaultFromFormData(oldData, ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr(), 0);
            Integer oldSerialNumberControl = getIntegerOrDefaultFromFormData(oldData, ProductEnum.ENABLE_SERIAL_NUMBER.getAttr(), 0);
            boolean oldShelfLifeDay = StringUtil.isEmpty(oldData.getString(ProductEnum.SHELF_LIFE_DAYS.getAttr()));
            boolean shelfLifeDay = StringUtil.isEmpty(data.getString(ProductEnum.SHELF_LIFE_DAYS.getAttr()));
            if (!Objects.equals(oldBatchControl, batchControl)
                    || !Objects.equals(serialNumberControl, oldSerialNumberControl)
                    || (!Objects.equals(oldShelfLifeDay, shelfLifeDay))){

                BoolQueryBuilder boolQueryBuilder = boolQuery();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
                boolQueryBuilder.filter(termQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.PARENT_ID), dataId));
                BoolQueryBuilder innerBoolQueryBuilder = boolQuery();
                innerBoolQueryBuilder.should(rangeQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.STOCK)).lt(0));
                innerBoolQueryBuilder.should(rangeQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.STOCK)).gt(0));
                innerBoolQueryBuilder.minimumShouldMatch(1);
                boolQueryBuilder.filter(innerBoolQueryBuilder);
                List<ProductEntityExt> list = paasEsModel.getDataList(boolQueryBuilder, IndexTypeEnum.IDX_SAAS_PRODUCT,
                        new ArrayList<>(), 1, 1, ProductEntityExt.class, null, null);
                if (CollectionsUtil.isNotEmpty(list)) {
                    String message = "";
                    if (oldBatchControl == 0 && batchControl == 1){
                        message = BatchSeqOperationEnum.ENABLEBATCH.getName();
                    }else if (oldBatchControl == 1 && batchControl == 0){
                        message = BatchSeqOperationEnum.DISABLEBATCH.getName();
                    }else if (oldSerialNumberControl == 0 && serialNumberControl == 1){
                        message = BatchSeqOperationEnum.ENABLESEQ.getName();
                    }else if (oldSerialNumberControl == 1 && serialNumberControl == 0){
                        message = BatchSeqOperationEnum.DISABLESEQ.getName();
                    }else if (oldShelfLifeDay && !shelfLifeDay){
                        message = BatchSeqOperationEnum.ENABLEGUARANTEEPERIOD.getName();
                    }else if (!oldShelfLifeDay && shelfLifeDay){
                        message = BatchSeqOperationEnum.DISABLEGUARANTEEPERIOD.getName();
                    }

                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205075, String.format(ProductErrorCodeEnum.API_ERROR_205075.getMsg(), productName, message));
                }

            }
            // 覆盖导入校验一下规格，也不知道对不对
            if (Objects.equals(validateDataDTO.getIsImport(), 1)) {
                boolean advancedMode = specObj.getBoolean(ProductConstant.ADVANCED_MODE);
                BoolQueryBuilder boolQueryBuilder = boolQuery();
                boolQueryBuilder.filter(termQuery("corpid.keyword", corpid));
                boolQueryBuilder.filter(termQuery(StringConstant.DEL, DelEnum.NORMAL.getDel()));
                boolQueryBuilder.filter(termQuery(ProductEnum.getEsAttr4Keyword(ProductEnum.PARENT_ID), dataId));
                List<String> fieldList = new ArrayList<>();
                fieldList.add(StringConstant.DATA_ID);
                fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION_LINK_TEXT));
                fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.COST));
                fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.ENABLE_SKU));
                fieldList.add(ProductEnum.getAttrConnectData(ProductEnum.SPECIFICATION));
                List<ProductEntityExt> childProduct = esHelper.findByScroll(IndexTypeEnum.IDX_SAAS_PRODUCT, boolQueryBuilder, ProductEntityExt.class, fieldList);
                Map<Long, PaasFormDataEntityExt> childMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                for (PaasFormDataEntityExt paasFormDataEntityExt : childProduct) {
                    childMap.put(paasFormDataEntityExt.getDataId(), paasFormDataEntityExt);
                }

                if (parentProduct != null && childProduct.size() == 1) {
                    //导入单规格保证产品成本不变,21696 【成功部】产品成本会被覆盖成0[图片加截图]
                    data.put(ProductEnum.COST.getAttr(), childProduct.get(0).getData().get(ProductEnum.COST.getAttr()));
                }
                List<SpecificationEntity> specificationList = specificationModel.getSpecificationList(corpid, dataId);
                Map<Long, Map<String, String>> specStringMap = new HashMap<>(childProduct.size());
                for (ProductEntityExt productEntityExt : childProduct) {
                    JSONObject json = productEntityExt.getData();
                    String spec = FastJsonHelper.getStringOrDefaultFromFormData(json, ProductEnum.SPECIFICATION_LINK_TEXT.getAttr(), "{}");
                    Integer isSpec = getIntegerOrDefaultFromFormData(json, ProductEnum.SPECIFICATION.getAttr(), 0);
                    //判断是单规格还是多规格
                    if (Objects.equals(isSpec,1)) {
                        Map<String, String> specMap = JSONObject.parseObject(spec,new TypeReference<LinkedHashMap<String, String>>() {});
                        specStringMap.put(productEntityExt.getDataId(), specMap);
                    }
                }
                List<Long> warehouseIdList = warehouseModel.getWarehouseIdsByPermission(validateDataDTO.getLoginUser(), validateDataDTO.getLoginUser().getDepSet());
                warehouseIdList.add(ProductConstant.ALL_WAREHOUSE_ID);
                fieldList = Arrays.asList(ProductWarehouseSubFormEnum.getAttrConnectDataByDbAttr("warehouseId"), ProductWarehouseSubFormEnum.getAttrConnectDataByDbAttr("warehouseName"), "id", ProductWarehouseSubFormEnum.getAttrConnectDataByDbAttr("stockUpperLimit"), ProductWarehouseSubFormEnum.getAttrConnectDataByDbAttr("stockLowerLimit"), ProductWarehouseSubFormEnum.getAttrConnectDataByDbAttr("isNotify"), ProductWarehouseSubFormEnum.getAttrConnectDataByDbAttr("productId"));
                List<ProductWarehouseEntity> warehouseRelationList = productWarehouseModel.getProductWareHouseByParentIdAndWarehouseIdIn(corpid, validateDataDTO.getDataId(), warehouseIdList, fieldList, 1);
                JSONArray notifySubFormArr = new JSONArray();
                if (advancedMode) {
                    JSONArray childProductArray = specObj.getJSONArray(ProductConstant.MUTIL_SPEC_DETAILS);
                    if (childProduct.size() > childProductArray.size()) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205060);
                    }
                    List<Long> pageChildIdList = new ArrayList<>();
                    for (int i = 0; i < childProductArray.size(); i++) {
                        JSONObject productObject = childProductArray.getJSONObject(i);
                        Long childProductId = null;
                        Map<String, String> specMap = JSONObject.parseObject(productObject.getString(ProductConstant.SPEC_TYPE),new TypeReference<LinkedHashMap<String, String>>() {});
                        for (Map.Entry<Long, Map<String, String>> entry : specStringMap.entrySet()) {
                            if (Objects.equals(entry.getValue(), specMap)) {
                                childProductId = entry.getKey();
                                pageChildIdList.add(childProductId);
                                break;
                            }
                        }
                        if (Objects.isNull(childProductId) && specMap.size() > 1) {
                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205060);
                        } else {
                            productObject.put(ProductConstant.CHILD_PRODUCT_ID, childProductId);
                            PaasFormDataEntityExt paasFormDataEntityExt = childMap.getOrDefault(childProductId, new PaasFormDataEntityExt());
                            specObj.getJSONArray(ProductConstant.MUTIL_SPEC_DETAILS).getJSONObject(i).put(ProductConstant.ENABLE, getIntegerOrDefaultFromFormData(paasFormDataEntityExt.getData(), ProductEnum.ENABLE_SKU.getAttr(), 1));
                            if (Objects.nonNull(childProductId)) {
                                specObj.getJSONArray(ProductConstant.MUTIL_SPEC_DETAILS).getJSONObject(i).put(ProductEnum.COST.getAttr(), getDoubleOrDefaultFromFormData(paasFormDataEntityExt.getData(), ProductEnum.COST.getAttr(), 0D));
                            }
                        }
                    }
                    if (!pageChildIdList.containsAll(childMap.keySet())) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205060);
                    }
                    data.put(ProductEnum.SPECIFICATION.getAttr(), specObj);
                    for (ProductWarehouseEntity productWarehouseEntity : warehouseRelationList) {
                        if (Objects.equals(productWarehouseEntity.getIsNotify(), 1)) {
                            JSONObject notifySubFormObj = new JSONObject();
                            PaasFormDataEntityExt paasFormDataEntityExt = childMap.getOrDefault(productWarehouseEntity.getProductId(), new PaasFormDataEntityExt());
                            JSONArray specArr = new JSONArray();
                            JSONObject specObjSaasUpdateHelp = FastJsonHelper.getJsonObjectOrDefaultFromFormData(paasFormDataEntityExt.getData(), ProductEnum.SPECIFICATION_LINK_TEXT.getAttr(), new JSONObject());
                            Map<String, String> specMap = JSONObject.parseObject(specObjSaasUpdateHelp.toJSONString(), new TypeReference<Map<String, String>>() {
                            });
                            for (SpecificationEntity specificationEntity : specificationList) {
                                if (specMap.containsKey(specificationEntity.getName())) {
                                    JSONObject spec = new JSONObject();
                                    spec.put(specificationEntity.getName(), specMap.get(specificationEntity.getName()));
                                    specArr.add(spec);
                                }
                            }
                            JSONArray warehouseArr = new JSONArray();
                            JSONObject warehouseObj = new JSONObject();
                            warehouseObj.put(StringConstant.SAAS_LINK_BUSINESS_ID, productWarehouseEntity.getWarehouseId());
                            warehouseObj.put(StringConstant.SAAS_LINK_BUSINESS_NAME, productWarehouseEntity.getWarehouseName());
                            warehouseArr.add(warehouseObj);
                            notifySubFormObj.put(ProductNotifyEnum.WAREHOUSE_NAME.getAttr(), warehouseArr);
                            notifySubFormObj.put(ProductNotifyEnum.SPECIFICATION.getAttr(), specArr);
                            notifySubFormObj.put(ProductNotifyEnum.STOCK_LOWER_LIMIT.getAttr(), productWarehouseEntity.getStockLowerLimit());
                            notifySubFormObj.put(ProductNotifyEnum.STOCK_UPPER_LIMIT.getAttr(), productWarehouseEntity.getStockUpperLimit());
                            notifySubFormArr.add(notifySubFormObj);
                        }
                    }
                } else {
                    if (!Objects.equals(childProduct.size(), 1)) {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205060);
                    }
                    for (ProductWarehouseEntity productWarehouseEntity : warehouseRelationList) {
                        if (Objects.equals(productWarehouseEntity.getIsNotify(), 1)) {
                            JSONObject notifySubFormObj = new JSONObject();
                            PaasFormDataEntityExt paasFormDataEntityExt = childMap.getOrDefault(productWarehouseEntity.getProductId(), new PaasFormDataEntityExt());
                            JSONArray specArr = new JSONArray();
                            String spec = FastJsonHelper.getStringOrDefaultFromFormData(paasFormDataEntityExt.getData(), ProductEnum.SPECIFICATION_LINK_TEXT.getAttr(), "");
                            JSONObject obj = new JSONObject();
                            obj.put(spec, spec);
                            specArr.add(obj);
                            JSONArray warehouseArr = new JSONArray();
                            JSONObject warehouseObj = new JSONObject();
                            warehouseObj.put(StringConstant.SAAS_LINK_BUSINESS_ID, productWarehouseEntity.getWarehouseId());
                            warehouseObj.put(StringConstant.SAAS_LINK_BUSINESS_NAME, productWarehouseEntity.getWarehouseName());
                            warehouseArr.add(warehouseObj);
                            notifySubFormObj.put(ProductNotifyEnum.WAREHOUSE_NAME.getAttr(), warehouseArr);
                            notifySubFormObj.put(ProductNotifyEnum.SPECIFICATION.getAttr(), specArr);
                            notifySubFormObj.put(ProductNotifyEnum.STOCK_LOWER_LIMIT.getAttr(), productWarehouseEntity.getStockLowerLimit());
                            notifySubFormObj.put(ProductNotifyEnum.STOCK_UPPER_LIMIT.getAttr(), productWarehouseEntity.getStockUpperLimit());
                            notifySubFormArr.add(notifySubFormObj);
                        }
                    }
                }
                data.put(ProductEnum.STOCK_NOTIFY.getAttr(), notifySubFormArr);
                if (!notifySubFormArr.isEmpty()) {
                    data.put(ProductEnum.IS_NOTIFY.getAttr(), 1);
                }
            }else {
                // 编辑产品对规格进行校验
                if (Objects.nonNull(specObj)) {
                    boolean advancedMode = specObj.getBoolean(ProductConstant.ADVANCED_MODE);
                    if (advancedMode) {
                        JSONArray specificationArray = specificationModel.getSpecificationArray(corpid, dataId);
                        int oldSpecSize = 1;
                        for (int i = 0;i < specificationArray.size();i++){
                            oldSpecSize = oldSpecSize * specificationArray.getJSONObject(i).getJSONArray(StringConstant.VALUE).size();
                        }
                        JSONArray specValueArray = specObj.getJSONArray("specValue");
                        int specValueCount = 1;
                        for (int i = 0; i < specValueArray.size(); i++) {
                            JSONObject specValueObj = specValueArray.getJSONObject(i);
                            JSONArray valueArray = specValueObj.getJSONArray("value");
                            specValueCount = specValueCount * valueArray.size();
                        }
                        if ((specValueCount > specValueMaxSize) && !Objects.equals(oldSpecSize, specValueCount)) {
                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205059);
                        }
                    }
                }
            }
            //编辑产品对多单位切换校验
            if (!enableMultiUnit) {
                if (Objects.equals(oldData.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getAttr()), BasicConstant.ONE)) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205097);
                }
            }else {
                if (Objects.equals(oldData.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getAttr()), BasicConstant.ONE) &&
                        !Objects.equals(oldData.get(ProductEnum.UNIT.getAttr()), data.get(ProductEnum.UNIT.getAttr()))) {
                    throw new XbbException(ProductErrorCodeEnum.API_ERROR_205096);
                }
            }
        }
    }

    /**
     * 产品编号唯一性校验
     * @param productNo 产品编号
     * @param corpid 公司id
     * @param dataId 数据id
     * @return true:没有重复，false：有重复
     * @throws XbbException
     */
    public Boolean productNoRepeat(String productNo, String corpid, Long dataId) throws XbbException{
        if (productNo == null || corpid == null) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002);
        }
        if (dataId == null) {
            dataId = 0L;
        }
        // 产品编号判重校验
        FormDataRepeatDTO formDataRepeatDTO = new FormDataRepeatDTO();
        formDataRepeatDTO.setAttr(ProductEnum.PRODUCT_NO.getAttr());
        formDataRepeatDTO.setValue(productNo);
        formDataRepeatDTO.setBusinessType(XbbRefTypeEnum.PRODUCT.getCode());
        formDataRepeatDTO.setSaasMark(SaasMarkEnum.SAAS.getCode());
        formDataRepeatDTO.setCorpid(corpid);
        formDataRepeatDTO.setDel(DelEnum.NORMAL.getDel());
        // 拼接条件，父产品id=0，从父产品的角度进行编号查重
        ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt();
        conditionsEntityExt.setAttr(ProductEnum.PARENT_ID.getAttr());
        conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
        conditionsEntityExt.setValue(Arrays.asList(0));
        conditionsEntityExt.setFieldType(FieldTypeEnum.NUM.getType());
        FormDataRepeatVO formDataRepeatVO = commonHelp.isUniqueByConditions(formDataRepeatDTO, Collections.singletonList(conditionsEntityExt));
        List<PaasFormDataEntityExt> formDataList = formDataRepeatVO.getFormDataList();
        if (formDataList.size() > 1) {
            return false;
        } else {
            if (!formDataList.isEmpty()) {
                PaasFormDataEntityExt paasFormDataEsEntity = formDataList.get(0);
                Long esDataId = paasFormDataEsEntity.getDataId();
                if (!Objects.equals(esDataId, dataId)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 校验产品修改成本
     * 对开启过进销存的公司进行判断
     * 1. 产品有过出入库记录的，不允许直接在产品页编辑成本(提示去使用成本调整单编辑产品)。
     * 2. 产品没有过出入库记录的，允许直接在产品页编辑成本。
     *
     * @param productEntityExt 产品信息
     * @param newData          前端修改的值
     * @throws XbbException 异常
     */
    private void checkProductCost(ProductEntityExt productEntityExt, JSONObject newData) throws XbbException {
        newData.remove(ProductConstant.UPDATE_COST);
        // 公司ID
        String corpid = productEntityExt.getCorpid();
        // 父产品ID
        Long parentProductId = productEntityExt.getId();
        // 找到对应的子产品信息
        List<PaasFormDataEntityExt> childProductList = this.findChildProductList(corpid, Collections.singletonList(parentProductId));
        if (CollectionsUtil.isEmpty(childProductList)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018);
        }
        // 判断是否开启过进销存
        Boolean isJxcUse = paasAppModel.validateAppEnable(null, XbbRefTypeEnum.JXC.getAlias(), SaasMarkEnum.SAAS.getCode(), corpid);
        // 未开启过进销存的公司不管
        if (!isJxcUse) {
            return;
        }
        // 根据子产品ID查询库存流水表是否存在数据，存在就说明改产品出入库过
        List<Long> childProductIdList = childProductList.stream().map(PaasFormDataEntityExt::getId).collect(Collectors.toList());
        Boolean existProduct = stockFlowBillModel.isExistProductIn(corpid, childProductIdList);
        // 如果不存在流水记录，就不管
        if (!existProduct) {
            return;
        }
        // 1.存在流水记录时把所有产品的成本改为原本的产品成本。
        // 2.前端默认已经把修改按钮隐藏了，这样时为了防止客户修改前端样式修改成本。
        // 上线前改动：修改产品时，需要删除成本信息
        JSONObject newSpecification = FastJsonHelper.getJsonObjectOrDefaultFromFormData(newData, ProductEnum.SPECIFICATION.getAttr(), new JSONObject());
        // 多规格处理
        if (newSpecification.containsKey(ProductConstant.MUTIL_SPEC_DETAILS)) {
            Map<Long, JSONObject> childProductMap = childProductList.stream().collect(Collectors.toMap(PaasFormDataEntityExt::getId, PaasFormDataEntity::getData));
            JSONArray newMutilSpecDetails = newSpecification.getJSONArray(ProductConstant.MUTIL_SPEC_DETAILS);
            for (int i = 0; i < newMutilSpecDetails.size(); i++) {
                JSONObject specDetail = newMutilSpecDetails.getJSONObject(i);
                // 只有产品ID在数据库存在才修改成原来的成本
                if (childProductMap.containsKey(specDetail.getLong(ProductConstant.CHILD_PRODUCT_ID))) {
                    JSONObject jsonObject = childProductMap.get(specDetail.getLong(ProductConstant.CHILD_PRODUCT_ID));
                    specDetail.put(ProductEnum.COST.getAttr(), jsonObject.get(ProductEnum.COST.getAttr()));
                }
            }
            newData.put(ProductEnum.SPECIFICATION.getAttr(), newSpecification);
        } else {
            JSONObject data = childProductList.get(0).getData();
            newData.put(ProductEnum.COST.getAttr(), data.get(ProductEnum.COST.getAttr()));
        }
    }

    /**
     * 根据公司ID判断是否开启过进销存
     *
     * @param corpid 公司ID
     * @return true -> 开启，false -> 未开启
     */
    private Boolean isOpenJXC(String corpid) {
        // 判断公司积分开启过进销存
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("del", 0);
        param.put("corpid", corpid);
        param.put("alias", XbbRefTypeEnum.JXC.getAlias());
        param.put("enable", EnableEnum.OPEN.getCode());
        List<PaasAppEntity> entitys = paasAppModel.findEntitys(param);
        return CollectionsUtil.isNotEmpty(entitys);
    }

    /**
     * 根据父产品ID找到对应的子产品信息
     *
     * @param corpid          公司ID
     * @param parentProductId 父产品ID集合
     * @return 子产品信息集合
     * @throws XbbException xbb异常
     */
    private List<PaasFormDataEntityExt> findChildProductList(String corpid, List<Long> parentProductId) throws XbbException {
        ChildProductDTO childProductDTO = new ChildProductDTO();
        childProductDTO.setCorpid(corpid);
        childProductDTO.setParentProductId(parentProductId);
        PaasFormDataEsListVO childProductList = formDataEsService.getChildProductList(childProductDTO);
        if (Objects.isNull(childProductList) || CollectionsUtil.isEmpty(childProductList.getPaasFormDataESList())) {
            return new ArrayList<>();
        }
        return childProductList.getPaasFormDataESList();
    }

    private void removeDataKey(JSONObject newData){
        newData.remove(ProductConstant.UPDATE_COST);
    }

}
